﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Networking.Sockets;
using Windows.Networking.Connectivity;
using Windows.ApplicationModel.DataTransfer;
using Windows.Networking;
using System.Text.RegularExpressions;
using System.IO;
using System.Runtime.Serialization;
using Windows.Storage;
using eLuk.Entity;

namespace eLuk.CommandEngine
{
	public class EmailClient
	{
		public eLukFolder RootFolder { get; set; }
		internal IMAPCommandEngine Engine { get; set; }
		IMAPCommands IMAPCommands { get; set; }
		public EmailClient()
		{
			this.IMAPCommands = new IMAPCommands();
			this.RootFolder = new eLukFolder( "" );
			this.RootFolder.HasSubfolder = true;
		}
		public EmailClient( EmailServer _server )
			: this()
		{
			//this.Engine = new IMAPCommandEngine( _server );
		}
		public bool CheckCapability()
		{
			try
			{
				IMAPCommand capability = new IMAPCommand( "CAPABILITY", "CAPABILITY" );
				this.Engine.ExecuteAsync( capability );
				return true;
			}
			catch ( Exception ex )
			{
				string str = ex.Message;
				return false;
			}
		}
		public bool UpdateCapability( string capabilities )
		{
			bool result = false;
			//IMAPCommandResponse response = new IMAPCommandResponse();
			try
			{
				if ( this.Engine.Channel.Server.Capability != null )
				{
					this.Engine.Channel.Server.Capability = null;
				}
				this.Engine.Channel.Server.Capability = new IMAPCapability( capabilities );
				result = true;
			}
			catch ( Exception ex )
			{
				result = false;
			}
			return result;
		}
		public bool Login()
		{
			try
			{
				IMAPCommand login = new IMAPCommand( "LOGIN", "LOGIN \"{0}\" \"{1}\"" );
				string commandResult = this.Engine.Execute( login, this.Engine.Channel.Server.Credential.User, this.Engine.Channel.Server.Credential.Password );
				this.UpdateCapability( commandResult );
				return true;
			}
			catch ( Exception ex )
			{
				string str = ex.Message;
				return false;
			}
		}
		public bool List()
		{
			try
			{
				IMAPCommand list = new IMAPCommand( "LIST", "LIST \"{0}/\" {1}" );
				string result =
				this.Engine.Execute( list, "", "%" );
				this.PrepareFolder( this.RootFolder, result );
				return true;
			}
			catch ( Exception ex )
			{
				string str = ex.Message;
				return false;
			}
		}
		public bool Select( eLukFolder folder, bool iterateSubFolder )
		{
			try
			{
				IMAPCommand select = new IMAPCommand( "SELECT", "SELECT \"{0}\"" );
				IMAPCommand search = new IMAPCommand( "UID SEARCH", "UID SEARCH {0}" );

				if ( iterateSubFolder && folder.HasSubfolder )
				{
					for ( int i = 0; i < folder.SubFolders.Count; i++ )
					{
						if ( folder.SubFolders[ i ].HasSubfolder )
						{
							this.Select( folder.SubFolders[ i ], iterateSubFolder );
						}
						
						string result = this.Engine.Execute( select, folder.SubFolders[ i ].Name );
						if ( result.Trim() != "" && result.StartsWith( "* FLAGS" ) )
						{
							this.UpdateFolderProperties( folder.SubFolders[ i ], result );
							
							result = this.Engine.Execute( search, "ALL" );
							this.UpdateMessages( folder.SubFolders[ i ], result );
						}
					}
				}
				else
				{
					string result = this.Engine.Execute( select, folder.Name );
					this.UpdateSelectResult( folder, result );
					result = this.Engine.Execute( search, "ALL" );
					this.UpdateMessages( folder, result );
				}
				return true;
			}
			catch ( Exception ex )
			{
				string str = ex.Message;
				return false;
			}
		}
		public bool Examine( eLukFolder folder )
		{
			try
			{
				for ( int i = 0; i < folder.SubFolders.Count; i++ )
				{
					if ( folder.SubFolders[ i ].HasSubfolder )
					{
						this.Examine( folder.SubFolders[ i ] );
					}
					IMAPCommand examine = new IMAPCommand( "EXAMINE", "EXAMINE \"{0}\"" );
					string result = this.Engine.Execute( examine, folder.SubFolders[ i ].Name );
					this.UpdateFolderProperties( folder.SubFolders[ i ], result );
				}
				return true;
			}
			catch ( Exception ex )
			{
				string str = ex.Message;
				return false;
			}
		}
		public bool FetchEnvelope( MailMessage message )
		{
			try
			{
				//int iTick = System.Environment.TickCount;
				IMAPCommand fetch = new IMAPCommand( "UID FETCH", "UID FETCH {0} ({1})" );
				string result = this.Engine.Execute( fetch, message.UID, "ENVELOPE INTERNALDATE RFC822.SIZE" );
				this.UpdateMessageEnvelope( message, result );

				return true;
			}
			catch ( Exception ex )
			{
				string str = ex.Message;
				return false;
			}
		}
		public bool Fetch( MailMessage message )
		{
			try
			{
				//int iTick = System.Environment.TickCount;
				IMAPCommand fetch = new IMAPCommand( "UID FETCH", "UID FETCH {0} ({1})" );
				string result = this.Engine.Execute( fetch, message.UID, "FULL" );

				this.UpdateMessage( message, result );

				return true;
			}
			catch ( Exception ex )
			{
				string str = ex.Message;
				return false;
			}
		}
		public void PrepareFolder( eLukFolder folder, string result )
		{
			string commandResult = result;
			eLukFolder currentFolder = null;
			string[] lines = commandResult.Split( new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries );

			foreach ( string line in lines )
			{
				Regex FolderParseRex = new Regex( Expressions.Folder, RegexOptions.IgnoreCase );
				Match match = FolderParseRex.Match( line );

				if ( match.Groups.Count == 4 )
				{
					currentFolder = new eLukFolder( match.Groups[ 3 ].Value );
					//folder.Name = ;
					//if ( match.Groups[ 2 ].Value == "/" )
					currentFolder.ParentFolder = folder;
					//currentFolder.ParentFolder = folder;

					currentFolder.HasSubfolder = !match.Groups[ 1 ].Value.Contains( "\\HasNoChildren" );

					if ( currentFolder.HasSubfolder )
					{
						IMAPCommand list = new IMAPCommand( "LIST", "LIST \"{0}/\" {1}" );
						commandResult = this.Engine.Execute( list, currentFolder.Name, "%" );
						this.PrepareFolder( currentFolder, commandResult );
					}
					else
					{

					}

					if ( folder.SubFolders == null )
						folder.SubFolders = new eLukFolders();
					folder.SubFolders.Add( currentFolder );
				}
			}
		}
		public async Task<bool> WriteClient()
		{

			bool result = true;

			MemoryStream memory = new MemoryStream();

			DataContractSerializer serializer = new
									DataContractSerializer( this.RootFolder.GetType() );

			StorageFolder folder = null;
			bool createFolder = false;
			bool createFile = false;
			StorageFile file = null;

			try
			{
				serializer.WriteObject( memory, this.RootFolder );
				memory.Position = 0;
				file = await ApplicationData.Current.LocalFolder.CreateFileAsync( "eluk.eluk" );

				try
				{
					folder = ApplicationData.Current.LocalFolder.GetFolderAsync( "eLuk" ).GetResults();
					createFolder = false;
				}
				catch ( Exception ex )
				{
					if ( ex is FileNotFoundException )
						createFolder = false;
				}
				if ( createFolder )
					folder = ApplicationData.Current.LocalFolder.CreateFolderAsync( "eLuk" ).GetResults();

				if ( folder != null )
				{
					try
					{
						file = folder.GetFileAsync( "eLuk.eLuk" ).GetResults();
						createFile = false;
					}
					catch ( Exception ex )
					{
						if ( ex is FileNotFoundException )
							createFile = true;
					}
				}

				if ( createFile )
					file = folder.CreateFileAsync( "eLuk.eLuk" ).GetResults();

				if ( file.IsAvailable )
				{
					using ( Stream fileStream = await file.OpenStreamForWriteAsync() )
					{
						memory.Seek( 0, SeekOrigin.Begin );
						await memory.CopyToAsync( fileStream );
						await fileStream.FlushAsync();

					}
				}
			}
			catch ( Exception ex )
			{

			}


			return result;

		}
		public async Task<bool> WriteProtectedClient()
		{
			//Windows.Security.Cryptography.CryptographicBuffer.
			bool result = true;

			MemoryStream memory = new MemoryStream();

			DataContractSerializer serializer = new
									DataContractSerializer( this.RootFolder.GetType() );

			StorageFolder folder = null;
			bool createFolder = false;
			bool createFile = false;
			StorageFile file = null;

			try
			{
				serializer.WriteObject( memory, this.RootFolder );
				memory.Position = 0;
				file = await ApplicationData.Current.LocalFolder.CreateFileAsync( "eluk.eluk" );

				try
				{
					folder = ApplicationData.Current.LocalFolder.GetFolderAsync( "eLuk" ).GetResults();
					createFolder = false;
				}
				catch ( Exception ex )
				{
					if ( ex is FileNotFoundException )
						createFolder = false;
				}
				if ( createFolder )
					folder = ApplicationData.Current.LocalFolder.CreateFolderAsync( "eLuk" ).GetResults();

				if ( folder != null )
				{
					try
					{
						file = folder.GetFileAsync( "eLuk.eLuk" ).GetResults();
						createFile = false;
					}
					catch ( Exception ex )
					{
						if ( ex is FileNotFoundException )
							createFile = true;
					}
				}

				if ( createFile )
					file = folder.CreateFileAsync( "eLuk.eLuk" ).GetResults();

				if ( file.IsAvailable )
				{
					using ( Stream fileStream = await file.OpenStreamForWriteAsync() )
					{
						memory.Seek( 0, SeekOrigin.Begin );
						await memory.CopyToAsync( fileStream );
						await fileStream.FlushAsync();
					}
				}
			}
			catch ( Exception ex )
			{

			}


			return result;

		}
		public async Task<eLukFolder> ReadClient()
		{
			DataContractSerializer serializer = new
									DataContractSerializer( this.RootFolder.GetType() );

			StorageFolder folder = null;
			StorageFile file = null;
			eLukFolder imapFolder = null;
			try
			{
				try
				{
					folder = ApplicationData.Current.LocalFolder.GetFolderAsync( "eLuk" ).GetResults();
				}
				catch ( Exception ex )
				{
					if ( ex.Message.Contains( "Method was called" ) )
					{

					}
				}

				if ( folder == null )
					folder = ApplicationData.Current.LocalFolder.GetFolderAsync( "eLuk" ).GetResults();
				
				if ( folder != null )
				{
					using ( Stream stream = await folder.OpenStreamForReadAsync( "eLuk.eLuk" ) )
					{
						byte[] bytes = new byte[ stream.Length ];
						stream.Read( bytes, 0, ( int ) stream.Length - 1 );

						//ClientSecurity cs = new ClientSecurity();
						//bytes = cs.Encrypt( bytes );
						object o = serializer.ReadObject( stream );
						imapFolder = o as eLukFolder;
					}
				}
				else
				{
					return null;
				}
			}
			catch ( Exception ex )
			{
				return null;
			}
			return imapFolder;
		}
		private bool UpdateFolderProperties( eLukFolder folder, string examinResponse )
		{
			bool result = false;

			string[] lines = examinResponse.Split( new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries );
			Regex regEx = null;
			Match match = null;
			string str = "";
			string[] strings;

			if ( lines.Length == 7 )
			{
				if ( lines[ 0 ].Contains( "* FLAGS " ) )
				{
					regEx = new Regex( Expressions.BetweenParanthesis, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 0 ] );
					folder.FolderFlags = eLuk.Entity.FolderFlags.Answered;
					if ( match.Groups.Count == 2 )
					{
						str = match.Groups[ 1 ].Value;
						strings = str.Split( " ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
						foreach ( string str1 in strings )
						{
							str = str1.Replace( "\\", "" );
							str = str.Replace( "$", "" );
							if ( Enum.GetNames( typeof( FolderFlags ) ).Contains( str ) )
							{
								if ( folder.FolderFlags == 0 )
									folder.FolderFlags = ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str );
								else
									folder.FolderFlags = folder.FolderFlags & ( ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str ) );
							}
						}
					}
				}

				if ( lines[ 1 ].Contains( "PERMANENTFLAGS" ) )
				{
					regEx = new Regex( Expressions.FolderPermanentFlags, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 1 ] );
					if ( match.Groups.Count == 2 )
					{
						str = match.Groups[ 1 ].Value;
						strings = str.Split( " ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
						foreach ( string str1 in strings )
						{
							str = str1.Replace( "\\", "" );
							str = str.Replace( "$", "" );
							if ( Enum.GetNames( typeof( FolderFlags ) ).Contains( str ) )
							{
								if ( folder.FolderFlags == 0 )
									folder.FolderFlags = ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str );
								else
									folder.FolderFlags = folder.FolderFlags & ( ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str ) );
							}
						}
					}

				}

				if ( lines[ 2 ].Contains( "UIDVALIDITY" ) )
				{
					regEx = new Regex( Expressions.FolderUIValidity, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 2 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.UIDValidatity = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 3 ].Contains( "EXISTS" ) )
				{
					regEx = new Regex( Expressions.FolderExists, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 3 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.Exists = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 4 ].Contains( "RECENT" ) )
				{
					regEx = new Regex( Expressions.FolderRecent, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 4 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.Recent = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 5 ].Contains( "UIDNEXT" ) )
				{
					regEx = new Regex( Expressions.FolderUIDNext, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 5 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.UIDNext = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 6 ].Contains( "HIGHESTMODSEQ" ) )
				{
					regEx = new Regex( Expressions.FolderHighestModSequence, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 6 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.HighestModSequence = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				//lines[0]
			}
			return result;
		}
		private bool UpdateSelectResult( eLukFolder folder, string selectResponse )
		{
			bool result = false;

			string[] lines = selectResponse.Split( new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries );
			Regex regEx = null;
			Match match = null;
			string str = "";
			string[] strings;

			if ( lines.Length == 7 )
			{
				if ( lines[ 0 ].Contains( "* FLAGS " ) )
				{
					regEx = new Regex( Expressions.BetweenParanthesis, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 0 ] );
					if ( match.Groups.Count == 2 )
					{
						str = match.Groups[ 1 ].Value;
						strings = str.Split( " ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
						foreach ( string str1 in strings )
						{
							str = str1.Replace( "\\", "" );
							str = str.Replace( "$", "" );
							if ( Enum.GetNames( typeof( FolderFlags ) ).Contains( str ) )
							{
								if ( folder.FolderFlags == 0 )
									folder.FolderFlags = ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str );
								else
									folder.FolderFlags = folder.FolderFlags & ( ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str ) );
							}
						}
					}
				}

				if ( lines[ 1 ].Contains( "PERMANENTFLAGS" ) )
				{
					regEx = new Regex( Expressions.FolderPermanentFlags, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 1 ] );
					if ( match.Groups.Count == 2 )
					{
						str = match.Groups[ 1 ].Value;
						strings = str.Split( " ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries );
						foreach ( string str1 in strings )
						{
							str = str1.Replace( "\\", "" );
							str = str.Replace( "$", "" );
							if ( Enum.GetNames( typeof( FolderFlags ) ).Contains( str ) )
							{
								if ( folder.FolderFlags == 0 )
									folder.FolderFlags = ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str );
								else
									folder.FolderFlags = folder.FolderFlags & ( ( FolderFlags ) Enum.Parse( typeof( FolderFlags ), str ) );
							}
						}
					}

				}

				if ( lines[ 2 ].Contains( "UIDVALIDITY" ) )
				{
					regEx = new Regex( Expressions.FolderUIValidity, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 2 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.UIDValidatity = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 3 ].Contains( "EXISTS" ) )
				{
					regEx = new Regex( Expressions.FolderExists, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 3 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.Exists = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 4 ].Contains( "RECENT" ) )
				{
					regEx = new Regex( Expressions.FolderRecent, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 4 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.Recent = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 5 ].Contains( "UIDNEXT" ) )
				{
					regEx = new Regex( Expressions.FolderUIDNext, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 5 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.UIDNext = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				if ( lines[ 6 ].Contains( "HIGHESTMODSEQ" ) )
				{
					regEx = new Regex( Expressions.FolderHighestModSequence, RegexOptions.IgnoreCase );
					match = regEx.Match( lines[ 6 ] );
					if ( match.Groups.Count == 2 )
					{
						folder.HighestModSequence = match.Groups[ 1 ].Value == "" ? 0 : Convert.ToInt64( match.Groups[ 1 ].Value );
					}
				}

				//lines[0]
			}
			return result;
		}
		private bool UpdateMessages( eLukFolder folder, string searchResult )
		{
			bool result = true;

			if ( searchResult.StartsWith( "* SEARCH " ) )
			{
				searchResult = searchResult.Replace( "* SEARCH ", "" );
				searchResult = searchResult.Replace( "\r\n", "" );
				string[] UIDs = searchResult.Split( new string[] { " " }, StringSplitOptions.RemoveEmptyEntries );
				MailMessage message = null;
				uint uuid = 0;
				foreach ( string uid in UIDs )
				{
					uuid = uint.Parse( uid );
					if ( folder.MailMessages == null )
						folder.MailMessages = new eLuk.Entity.MailMessages();
					else
						message = folder.MailMessages.Find( m => m.UID == uuid );
					if ( message == null )
					{
						message = new MailMessage();
						message.UID = uuid;
					}
					this.FetchEnvelope( message );
					//this.Fetch( message );
					folder.MailMessages.Add( message );
				}
			}

			return result;
		}
		private bool UpdateMessage( MailMessage message, string searchResult )
		{
			bool result = true;

			if ( searchResult.StartsWith( "* %d FETCH" ) )
			{
				searchResult = searchResult.Replace( "* SEARCH ", "" );
				searchResult = searchResult.Replace( "\r\n", "" );
				string[] UIDs = searchResult.Split( new string[] { " " }, StringSplitOptions.RemoveEmptyEntries );
				uint uuid = 0;
			}
			return result;
		}
		private bool UpdateMessageEnvelope( MailMessage message, string fetchResult )
		{
			bool result = true;

			if ( fetchResult.StartsWith( "* " ) && fetchResult.Contains( "FETCH" ) )
			{
				Regex regex = new Regex( "(RFC822.SIZE(.*)INTERNALDATE)|(INTERNALDATE(.*)ENVELOPE)|(\\\".*?\\\" \\\".*?\\\")|(<(.*)>)" );
				Regex regexMerger = new Regex( "\\\" \\\"" );
				MatchCollection matches = regex.Matches( fetchResult );
				string s = "";

				if ( matches.Count == 7 )
				{
					s = matches[ 0 ].Value.Replace( "RFC822.SIZE", "" ).Replace( "INTERNALDATE", "" );
					long _size = 0;
					if ( long.TryParse( s.Trim(), out _size ) )
						message.Size = _size;

					s = matches[ 1 ].Value;
					regex = new Regex( "(\\\".*?\\\" \\\".*?\\\")" );
					MatchCollection mc = regex.Matches( s );

					s = regexMerger.Replace( matches[ 2 ].Value, "@" ).Replace( "\"", "" ).Replace( "<", "" ).Replace( ">", "" );
					message.From = new MailAddress( s, s );

					s = regexMerger.Replace( matches[ 5 ].Value, "@" ).Replace( "\"", "" ).Replace( "<", "" ).Replace( ">", "" );
					message.To.Add( new MailAddress( s, s ) );

				}

				//fetchResult = fetchResult.Replace( "* SEARCH ", "" );
				//fetchResult = fetchResult.Replace( "\r\n", "" );
				//string[] UIDs = fetchResult.Split( new string[] { " " }, StringSplitOptions.RemoveEmptyEntries );
				//uint uuid = 0;
			}
			return result;
		}
		//internal bool UpdateFolderUID( IMAPFolder folder, string searchResult )
		//{
		//	bool result = true;

		//	if ( searchResult.StartsWith( "* FLAGS " ) )
		//	{
		//		searchResult = searchResult.Replace( "* SEARCH ", "" );
		//		searchResult = searchResult.Replace( "\r\n", "" );
		//		string[] UIDs = searchResult.Split( new string[] { " " }, StringSplitOptions.RemoveEmptyEntries );
		//		MailMessage message = null;
		//		uint uuid = 0;
		//		foreach ( string uid in UIDs )
		//		{
		//			uuid = uint.Parse( uid );
		//			if ( folder.MailMessages == null )
		//				folder.MailMessages = new eLuk.Entity.MailMessages();
		//			else
		//				message = folder.MailMessages.Find( m => m.UID == uuid );
		//			if ( message == null )
		//			{
		//				message = new MailMessage();
		//				message.UID = uuid;
		//				folder.MailMessages.Add( message );
		//			}
		//		}
		//	}

		//	return result;
		//}
		public bool LoadClient()
		{
			bool result = true;
			return result;
		}
		
	}
}